using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Engine.Business.Abstract;
using Engine.CSV;
using System.Data;
using System.IO;
using HunterPluginSDK;
using HunterPluginSDK.Interface;


namespace Engine.Factory
{
    public class CollectionFactory: Engine.Business.Interfaces.IHunterLog
    {
        #region STATUS FLAGS
        public bool ContinueHunting = false;
        public bool ContinueBackup = false;
        #endregion

        static string EntitySeparator = " -->Summary--> ";
        static private string NeededPath = "Needed\\";
        static public string Link = "link://";
        private DataSchema.CollectionSchema         MyColl;
        private Business.Model.PicturesCollection   MyPictureCollection;
        
        private IProgressHandler NotifyProgress;
        

        #region CTOR
        private CollectionFactory(DataSchema.CollectionSchema c, IProgressHandler ph)
        {
            MyColl = c;
            NotifyProgress = ph;
            MyPictureCollection = new Business.Model.PicturesCollection(
                ((DataSchema.CollectionSchema.CollectionRow)(c.Collection.Rows[0])).Name,
                ((DataSchema.CollectionSchema.CollectionRow)(c.Collection.Rows[0])).Description,
                ((DataSchema.CollectionSchema.CollectionRow)(c.Collection.Rows[0])).Site,
                ((DataSchema.CollectionSchema.CollectionRow)(c.Collection.Rows[0])).Path);
        }
        #endregion

        #region TOOLS
        public static string ExtractEntityName(string item)
        {
            int ind = item.IndexOf(EntitySeparator);
            if(ind > 0)
                return item.Substring(0, ind);
            else
                return item;
        }

        public static string BuildEntityListItem(DataSchema.CollectionSchema.CollectionEntityRow r)
        {
            return r.Name + EntitySeparator + "Total: " + r.Total.ToString() + " Hunted: " + r.Hunted.ToString();
        }

        public static string BuildInString(DataRow[] rs, string col)
        {
            string s = "(";
            foreach (DataRow r in rs)
            {
                s += "'" + r[col].ToString() + "'" + ",";
            }
            if (s.EndsWith(","))
                s = s.Substring(0, s.Length - 1);
            s += ")";
            return s;
        }

        public static string ExtractEntityFromCSVName(string csvname)
        {
            int i = csvname.IndexOf("(");
            if(i>=0)
                return csvname.Substring(0, i);
            return csvname.Substring(0, csvname.Length - 4);
        }



        #endregion

        #region MANAGE ITEM
        public static Business.Abstract.CollectionItem CreateNewCollectionItem()
        {
            return new Business.Model.Picture();
        }
        #endregion

        #region MANAGE COLLECTION


        public void Burn()
        {
            /*
            try
            {
                XPBurn.XPBurnCD burner = new XPBurnCD();
                burner.BurnerDrive = burner.RecorderDrives[0].ToString();
                string path2burn;
                foreach (DataSchema.CollectionSchema.CollectionEntityRow ce in MyColl.GetSelectedEntities())
                {
                    DataSchema.CollectionSchema.ShotRow[] srs = DataSchema.CollectionSchema.GetEntityShots(ce);
                    foreach (DataSchema.CollectionSchema.ShotRow sr in srs)
                    {
                        DataSchema.CollectionSchema.ItemRow[] irs = DataSchema.CollectionSchema.GetShotItems(sr);
                        foreach (DataSchema.CollectionSchema.ItemRow ir in irs)
                        {
                            if (ir.Exists)
                            {
                                path2burn = GetItemPath((DataSchema.CollectionSchema.CollectionRow)MyColl.Collection.Rows[0], ir);
                                burner.AddFile(path2burn, path2burn.Substring(2));
                            }
                        }
                    }
                }
                string s = burner.VolumeName;
                burner.BurnComplete += new NotifyCompletionStatus(burner_BurnComplete);
                burner.BlockProgress += new NotifyCDProgress(burner_BlockProgress);
                this.NotifyProgress.WriteToOutput("Burn Starts", Engine.Business.Interfaces.LogPriority.INFO);
                burner.RecordDisc(true, true);
            }
            catch (Exception exc)
            {
                string es = exc.ToString();
            }
             */
        }

        void burner_BlockProgress(int nCompletedSteps, int nTotalSteps)
        {
            this.NotifyProgress.WriteToOutput("Bunded " + nCompletedSteps + " from Total " + nTotalSteps , LogPriority.INFO);
        }

        void burner_BurnComplete(uint status)
        {
            this.NotifyProgress.WriteToOutput("Burn Complete", LogPriority.INFO);
        }

        #endregion

        #region MANAGE ENTITY
        public DataSchema.CollectionSchema.CollectionEntityRow
            CreateNewCollectionEntity(string csvf)
        {
            string entity =  ExtractEntityFromCSVName(csvf);

            DataSchema.CollectionSchema.CollectionEntityRow rEnt = MyColl.AddNewEntity(entity, csvf);
            CSV.CsvScanner scanner = new CSV.CsvScanner(csvf, ((DataSchema.CollectionSchema.CollectionRow)MyColl.Collection.Rows[0]).CSVFolderPath);

            string shot = "";
            int count = 0;
            uint shottotal = 0;
            DataSchema.CollectionSchema.ShotRow prevcsr = null;
            DataSchema.CollectionSchema.ShotRow csr = null;
            bool bPic = false;
            foreach(CollectionItem ci in scanner.Scan())
            {
                if(shot != ci.Description)//we shall create new shot
                {
                    prevcsr = csr;

                    bPic = DataSchema.CollectionSchema.IsItemPic(ci.Name);

                    csr = MyColl.AddNewShot(ci.Description, "", entity, ci.LinkedPath, bPic);
                        
                    shot = ci.Description;
                    if(prevcsr != null)
                    {
                        prevcsr.Total = shottotal;
                        shottotal = 0;
                    }
                    this.NotifyProgress.WriteToOutput("New shot " + csr.Name + " was added to " + csr.EntityName + " entity" , LogPriority.INFO);
                }

                DataSchema.CollectionSchema.ItemRow cir = 
                    MyColl.AddNewItem(ci.Name, ci.CRC32, ci.Size, shot, entity);

                ++count;
                ++shottotal;
             }
             if(csr != null)
             {
                 csr.Total = shottotal;
                 shottotal = 0;
             }
             
             rEnt.Total = (uint)count;

             return rEnt;
        }

        public void
            UpdateCollectionEntity()
        {
            MyColl.EnforceConstraints = false;
            List<DataSchema.CollectionSchema.CollectionEntityRow> delrs = new List<DataSchema.CollectionSchema.CollectionEntityRow>();
            
            foreach(DataSchema.CollectionSchema.CollectionEntityRow dr in MyColl.GetSelectedEntities())
            {
                delrs.Add(dr);
            }

            for(int i = 0; i< delrs.Count; ++i)
            {
                string csv = delrs[i].CSVFileName;
                delrs[i].Delete();
                CreateNewCollectionEntity(csv);
            }
            MyColl.EnforceConstraints = true;
            
            MyColl.AcceptChanges();
            //to mark collection as changed
            MyColl.Collection.Rows[0]["Name"] = MyColl.Collection.Rows[0]["Name"];
        }



        public void
            CreateNeededReport()
        {
            string colpath = ((DataSchema.CollectionSchema.CollectionRow)(MyColl.Collection.Rows[0])).Path + NeededPath;
            DirectoryInfo di = new DirectoryInfo(colpath);
            if (di.Exists == false)
                di.Create();

            foreach(DataSchema.CollectionSchema.CollectionEntityRow ce in MyColl.GetSelectedEntities())
            {
                StreamWriter sw = new StreamWriter(colpath + ce.Name + "_needed.csv");

                DataSchema.CollectionSchema.ItemRow[] rsi = DataSchema.CollectionSchema.GetEntityItems(ce);
                foreach(DataSchema.CollectionSchema.ItemRow ir in rsi)
                {
                    if(ir.Exists == false)
                    {

                        if (DataSchema.CollectionSchema.GetItemShot(ir).Pics ||
                            !DataSchema.Settings.SettingsMgr.NeededOnlyPictures)
                        {
                            string crc = string.Format("{0:X}", ir.CRC32);
                            sw.WriteLine(ir.Name + "," + ir.Size + "," + crc + "," + ir.Shot + ",Uknown");
                        }
                    }
                }
                sw.Close();
            }
        }

        static public void ExportToEntity(string path, List<string> items)
        {
            StreamWriter sw = new StreamWriter(path, true);
            foreach(string s in items)
            {
                sw.WriteLine(s);
            }
            sw.Close();
        }

        public void
            UpdateInternalList(DataSchema.CollectionSchema.ShotRow[] srows, 
            List<string> fi,
            bool link)
        {
            string linkedto, crc;
            foreach(DataSchema.CollectionSchema.ShotRow sr in srows)
            {
                DataSchema.CollectionSchema.ItemRow[] irows = DataSchema.CollectionSchema.GetShotItems(sr);
                linkedto = "";
                if(link)               
                    linkedto = Link + MyColl.GetEntityPath(DataSchema.CollectionSchema.GetShotEntity(sr)) + ",";
                foreach(DataSchema.CollectionSchema.ItemRow ir in irows)
                {
                    crc = string.Format("{0:X}", ir.CRC32);
                    fi.Add(ir.Name + "," + ir.Size + "," + crc + "," + ir.Shot + "," + linkedto);
                }
            }
        }
        
        
        
        public void RemoveEntity(string entname, DataSchema.CollectionSchema ds)
        {
            DataRow[] rs = ds.CollectionEntity.Select("Name = '" + entname + "'");
            if(rs != null)
            {
                foreach(DataRow r in rs)
                {
                    ds.CollectionEntity.Rows.Remove(r);
                }
            }
        }
        #endregion

        #region HUNT

        private void DoMyWork(object sender, DoWorkEventArgs e)
        {
            string currentent = "";


            List<Engine.CSV.CsvHuntInfo> hil = (List<Engine.CSV.CsvHuntInfo>)e.Argument;
            int hunted = 0;
            
            NotifyProgress.ProgressDesription = "Start hunting .... ";

            foreach (Engine.CSV.CsvHuntInfo hi in hil)
            {
                if(ContinueHunting == false)//user breaks operation
                    break;
                try
                {
                    currentent = hi.Entity.Name;

                    DataSchema.CollectionSchema.CollectionEntityRow[] r = (DataSchema.CollectionSchema.CollectionEntityRow[])
                        MyColl.CollectionEntity.Select("Name = '" + hi.Entity.Name + "'");

                    if (r.Length != 1)
                        throw new Exception("There is no collection available");

                    hi.CsvFilePath = ((DataSchema.CollectionSchema.CollectionRow)(MyColl.Collection.Rows[0])).CSVFolderPath;
                    hi.CsvFileName = ((DataSchema.CollectionSchema.CollectionEntityRow)(r[0])).CSVFileName;

                    hi.Entity.Path = MyColl.GetEntityPath(r[0]);

                    DataSchema.CollectionSchema.ItemRow[] rsi = DataSchema.CollectionSchema.GetEntityItems(r[0]);

                    if (hi.TypeofProcess == ProcessType.Verify)// if Verify clear Exists state for all items
                    {
                        MyColl.BeginValidation(r[0]);
                    }

                    Engine.CSV.CsvHunter h =
                        new Engine.CSV.CsvHunter(hi, new Engine.Factory.CollectionEntityScanner(rsi, this), NotifyProgress);

                    if (hi.TypeofProcess == ProcessType.Hunt)
                    {
                        hunted = h.Hunt(ref ContinueHunting);
                    }
                    else
                        hunted = h.Verify(ref ContinueHunting);

                }
                catch (Exception ex)
                {
                    NotifyProgress.WriteToOutput("Error " + ex.ToString(), LogPriority.CRITICAL);
                    ContinueHunting = false;
                }
            }
        }

        public void Hunt(ProcessType pr)
        {
            DateTime now = DateTime.Now;
            RunWorkerCompletedEventHandler completed = delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                NotifyProgress.ProgressDesription = "End Hunting, " + MyColl.LastHuntLog.Rows.Count + " items was processed";
                this.MyColl.UpdateSummary();
                NotifyProgress.TotalProgress = -1;
                DateTime now1 = DateTime.Now;
                TimeSpan ts = now1 - now;
                NotifyProgress.WriteToOutput("Hunting lasts " + ts.ToString(), LogPriority.INFO);
            };


            System.ComponentModel.BackgroundWorker backgroundWorker = null;

            List<Engine.CSV.CsvHuntInfo> HuntParams = new List<Engine.CSV.CsvHuntInfo>();
            int i = 0;

            foreach (DataSchema.CollectionSchema.CollectionEntityRow item 
                in MyColl.GetSelectedEntities())
            {
                Engine.CSV.CsvHuntInfo hi = new Engine.CSV.CsvHuntInfo();
                hi.Entity = new Engine.Business.Model.PicturesEntity(item.Name, MyPictureCollection);
                hi.TypeofProcess = pr;

                if (pr == CSV.ProcessType.Hunt)
                {
                    hi.FoldersToScan = new string[MyColl.GetSelectedIncomingFoldersCount()];
                    int j = 0;
                    foreach (string name in MyColl.GetSelectedIncomingFolders())
                    {
                        hi.FoldersToScan[j++] = name;
                    }
                }

                HuntParams.Add(hi);

                i++;
            }

            backgroundWorker = new BackgroundWorker();
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
            backgroundWorker.DoWork +=
                new System.ComponentModel.DoWorkEventHandler(DoMyWork);
            backgroundWorker.RunWorkerAsync(HuntParams);
        }

        #endregion

        #region BUCKUP
        private void DoMyBackup(object sender, DoWorkEventArgs e)
        {
            DataSchema.CollectionSchema.BackupRow backup = (DataSchema.CollectionSchema.BackupRow)this.MyColl.Backup.Rows[0];

            List<DataSchema.CollectionSchema.ShotRow> list = MyColl.GetShotsToBackUp();

            NotifyProgress.TotalProgress = list.Count;

            Engine.IO.FileType ft = Engine.IO.FileType.All;

            if (!backup.IsPicsNull() && !backup.IsVidsNull())
            {
                if (backup.Pics == true && backup.Vids == true)
                    ft = Engine.IO.FileType.All;
                else if (backup.Pics == true)
                    ft = Engine.IO.FileType.Pics;
                else if (backup.Vids == true)
                    ft = Engine.IO.FileType.Vids;

            }

            foreach (DataSchema.CollectionSchema.ShotRow shot in list)
            {
                if (this.ContinueBackup == false)
                    break;

                try
                {
                    string shotp = MyColl.GetShotPath(shot);
                    Engine.IO.FileTools.CopyFolder(shotp, backup.Folder, ft);
                    NotifyProgress.ProgressChanged();
                    NotifyProgress.ProgressDesription = "Backuping " + shot.Name + " from " + shot.EntityName;
                    ++_processeditems;
                }
                catch(Engine.IO.IOException iox)
                {
                    if(iox.Target == Engine.IO.Target.Source)
                        NotifyProgress.WriteToOutput("BackUp. No file to backup for " + iox.Path , LogPriority.INFO);
                    else
                        NotifyProgress.WriteToOutput("BackUp. " + iox.ToString(), LogPriority.CRITICAL);
                }
                catch(Exception ex)
                {
                    NotifyProgress.WriteToOutput("BackUp. " + ex.ToString(), LogPriority.CRITICAL);
                }
            }
        }

        private ulong _processeditems = 0;
        public void BackUp()
        {
            System.ComponentModel.BackgroundWorker backgroundWorker = null;
            backgroundWorker = new BackgroundWorker();
            DateTime now = DateTime.Now;

            RunWorkerCompletedEventHandler completed = delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                NotifyProgress.ProgressDesription = "End BackUp, " + _processeditems + " Shotss was Backuped";
                DateTime now1 = DateTime.Now;
                TimeSpan ts = now1 - now;
                NotifyProgress.WriteToOutput("BackUp lasts " + ts.ToString(), LogPriority.INFO);
                ContinueBackup = false;
            };

            _processeditems = 0;
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
            backgroundWorker.DoWork +=
                new System.ComponentModel.DoWorkEventHandler(DoMyBackup);
            backgroundWorker.RunWorkerAsync();
        }
        #endregion

        #region IHunterLog
        public void Log(Engine.Business.Abstract.CollectionItem ci, CSV.ItemState itemstate)
        {
            DataSchema.CollectionSchema.LastHuntLogRow lr = MyColl.LastHuntLog.NewLastHuntLogRow();

            lr.BeginEdit();
            lr.Item = ci.Name;
            lr.Body = ci.Description;
            lr.Info = itemstate.ToString();
            lr.Path = ci.OriginalPath;
            lr.Size = ci.Size;
            if(itemstate == ItemState.Error || itemstate == ItemState.Valid)
                lr.Selected = false;
            lr.EndEdit();

            MyColl.LastHuntLog.Rows.Add(lr);
        }
        #endregion

        #region STATIC FACTORY
        static public CollectionFactory CreateNewCollection(DataSchema.CollectionSchema ds, IProgressHandler ph,
            string name, string desc, string site, string csvpath, string colpath)
        {
            DataSchema.CollectionSchema.CollectionRow r = ds.Collection.NewCollectionRow();
            r.Name = name;
            r.Description = desc;
            r.DateTime = DateTime.Now;
            r.Site = site;
            r.CSVFolderPath = csvpath;
            r.Path = colpath;
            r.HuntPaths = "";
            ds.Collection.Rows.Add(r);
            return new Engine.Factory.CollectionFactory(ds, ph); ;
        }

        static public CollectionFactory OpenCollection(DataSchema.CollectionSchema ds, IProgressHandler ph)
        {
            return new Engine.Factory.CollectionFactory(ds, ph); ;
        }
        #endregion
    }
}
